﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
using System.Text.RegularExpressions;
using System.Net;

namespace devicesDll
{
    public class Switch:Device
    {
        #region Variables
        int snmpPort = 161;
        #endregion

        #region Constructor
        /// <summary>
        /// The Constructor from a Switch, every Switch needs a ip address and the vlan
        /// </summary>
        /// <param name="ip">Ip Address of a switch</param>
        /// <param name="vlan">This is the Vlan of witch Ports you want the Mac Address</param>  
        public Switch(IPAddress ip, Int64 vlan)
        {
            this.ip = ip;
            this.vlan = vlan;         
        }
        #endregion

        #region Properties
        /// <summary>
        /// Port for the SNMP Get methode
        /// </summary>
        public int SnmpPort
        {
            get
            {
                return snmpPort;
            }
            set
            {
                snmpPort = value;
            }
        }
        #endregion

        #region Methods
        
        /// <summary>
        /// Make a request to the switch. You will get all aktive Acccess Ports with their Mac Address from the Vlan you chose.
        /// </summary>
        /// <param name="switchOnly">This have to be true for the switch</param>
        /// <param name="query"></param>
        public override void Query(bool switchOnly, string query)
        {
            connectionFailed = false;
            if (!switchOnly)                //if SwitchOnly is true and the query value is default it makes a default switch request, else it returns.
                return;
            string[] splitquery = query.Split(';');
            foreach (string request in splitquery)
            {
                if (request == "default")
                    StandardRequest();
                else
                {
                    return;
                }
            }  
        }

        /// <summary>
        /// Makes the standardRequest of a switch
        /// </summary>
        private void StandardRequest()
        {
            string vlanconect = "public@" + this.vlan.ToString(); //Add the Vlan to the string, witch is needed for the snmp request

            var macList = new List<Variable>();         //these are the snmp lists for the different bridged lists
            var firstList = new List<Variable>();
            var secondList = new List<Variable>();
            var portList = new List<Variable>();
            var trunkList = new List<Variable>();       //this is the snmp list for the trunk ports
            SwitchCommands oids = new SwitchCommands();
            try     //Throw exception if the switch is not available
            {
                //This is one of the four snmp walk operator. With this you get the four tables to construct the cam table from the cisco switch
                Messenger.Walk(VersionCode.V1,                                  //Version Code of SNMP in this case it is V1
                                new IPEndPoint(this.ip, snmpPort),              //the ip Address of the switch and the prot for snmp
                                new OctetString(vlanconect),                    //select the vlan
                                new ObjectIdentifier(oids["MacAddressList"]),   //the object identifier to the maclist
                                macList,                                        //save the data in the maclist
                                timeout,                                        //waiting time bevore it throws an exception
                                WalkMode.WithinSubtree);                        //walk mode ??

                Messenger.Walk(VersionCode.V1,
                               new IPEndPoint(this.ip, snmpPort),
                               new OctetString(vlanconect),
                               new ObjectIdentifier(oids["FirstBridgeList"]),
                               firstList,
                               timeout,
                               WalkMode.WithinSubtree);

                Messenger.Walk(VersionCode.V1,
                               new IPEndPoint(this.ip, snmpPort),
                               new OctetString(vlanconect),
                               new ObjectIdentifier(oids["SeconBridgeList"]),
                               secondList,
                               timeout,
                               WalkMode.WithinSubtree);

                Messenger.Walk(VersionCode.V1,
                               new IPEndPoint(this.ip, snmpPort),
                               new OctetString(vlanconect),
                               new ObjectIdentifier(oids["Portlist"]),
                               portList,
                               timeout,
                               WalkMode.WithinSubtree);

                Messenger.Walk(VersionCode.V1,
                               new IPEndPoint(this.ip, snmpPort),
                               new OctetString("public"),
                               new ObjectIdentifier(oids["TrunkList"]),
                               trunkList,
                               timeout,
                               WalkMode.WithinSubtree);
            }
            catch                       //catch the exception from the request and give out a no connection at
            {
                connectionFailed = true;
                return;
            }

            List<string[]> tempPortMac = new List<string[]>();    //new temporary list to safe the port to mac list

            for (int i = 0; i < macList.Count; i++)             //for each maclist
            {
                for (int j = 0; j < firstList.Count; j++)       //for each firstlist
                {
                    if (filter(macList[i],'0') == filter(firstList[j],'0'))                //if a maclist number = a fistrlist number do...
                    {
                        for (int k = 0; k < secondList.Count; k++)                                  //for each secondlist
                        {
                            if (firstList[j].Data.ToString() == filter(secondList[k],'1'))    //if the fistlist data = secondlist number do...
                            {
                                for (int l = 0; l < portList.Count; l++)                            //for each portlist
                                {
                                    if (secondList[k].Data.ToString() == filter(portList[l],'1'))                       //if secondlist data = portlist number
                                    {
                                        for (int m = 0; m < trunkList.Count; m++)                                           //for each trunklist do...
                                        {
                                            if (filter(portList[l], '1') == filter(trunkList[m],'1'))               //if portlist number = trunklist number do
                                            {
                                                if (trunkList[m].Data.ToString() == "2")                                    //if trunklist data = 2 it is an access port if 1 it is a trunk port
                                                {
                                                    string[] a = { portList[l].Data.ToString(), Maclist_mac(macList[i]) };  //write a array a with port, mac
                                                    tempPortMac.Add(a);                                                     //add the array to a temporary list      
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                }
            }

            this.port = new string[tempPortMac.Count];
            this.mac = new string[tempPortMac.Count];

            for (int i = 0; i < tempPortMac.Count; i++) //save the temporary list in a port and mac array
            {
                this.port[i] = tempPortMac[i][0];
                this.mac[i] = tempPortMac[i][1].ToLower();
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string thisString = "Switch\n IP: " + this.ip.ToString() + "  Vlan: " + this.vlan + "\n Port-Mac: \n";
            if (this.mac != null)
            {
                for (int i = 0; i < this.mac.Length; i++)
                {
                    thisString += "  Mac: " + this.mac[i] + " Port: " + this.port[i] + "\n";
                }
            }
            else
            {
                thisString += " null\n";
            }
            return thisString;
        }

        //To edit different tables, to get the right value to compare 
        #region filter
        /// <summary>
        ///  This part Converts the mac Address in a usefull string
        /// </summary>
        /// <param name="var"></param>
        /// <returns>mac Address as a string</returns>
        private static string Maclist_mac(Variable var) //returns the Mac Address of this (1.3.6.1.2.1.17.4.3.1.1) SNMP walk
        {
            byte[] hex = var.Data.ToBytes();
            string mac = BitConverter.ToString(hex);
            mac = mac.Remove(0, 6);
            return mac;
        }

        /// <summary>
        /// This filters the OID String of a SNMP Request
        /// </summary>
        /// <param name="var">The OID String</param>
        /// <param name="shorten">is 0 or 1 to Filter the OID</param>
        /// <returns></returns>
        private static string filter(Variable var, char shorten)
        {
            string filterString = "";
            switch (shorten)
            {
                case '0':                                       //this case is used for the MacList and the FirstBridgeList
                    filterString = @"(?<data>(\.\d*){6})\;";
                    break;

                case '1':                                       //this case is used for the secondBridgeList, PortList and the Trunklist
                    filterString = @"\.(?<data>(\d*))\;";
                    break;
            }

            string objectString = var.ToString();
            Regex regex = new Regex(                            //This RegularExpression Option filters the OID of the Lists for each entry
            filterString,
            RegexOptions.IgnoreCase
            | RegexOptions.Multiline
            | RegexOptions.IgnorePatternWhitespace
            | RegexOptions.Compiled
            );
            objectString = regex.Match(objectString).Groups["data"].ToString();
            return objectString;
        }
        #endregion
        #endregion      
    }
}
